#include "ipDataCloud.h"
#include <iostream>
#include "string.h"
#include <stdlib.h>

//char* IP_FILENAME = "F:\\ipv4_city.dat";


geo_ip *ip_instance(const char *fileName) {
    static geo_ip *instance = NULL;
    if (instance == NULL) {
        instance = new geo_ip();
        if (ip_loadDat(instance, fileName) >= 0) {
            return instance;
        }
        if (instance == NULL) {
            delete instance;
        }
        return NULL;
    }
    return instance;
}

int32_t ip_loadDat(geo_ip *p, const char *fileName) {
    FILE *file;

    long len = 0;
    int k, i, j;
    file = fopen(fileName, "rb");
    if (file == NULL) {
        printf("%s", "打开文件错误");
        return -2;
    }
    fseek(file, 0, SEEK_END);
    len = ftell(file);
    fseek(file, 0, SEEK_SET);
    p->buffer = (uint8_t *) malloc(len * sizeof(uint8_t));
    fread(p->buffer, 1, len, file);
    fclose(file);

    for (k = 0; k < 256; k++) {
        i = k * 8 + 4;
        p->prefStart[k] = ip_read_int32(p, i);
        p->prefEnd[k] = ip_read_int32(p, i + 4);
    }

    return 0;
}

char *ip_query(geo_ip *p, const char *ip) {
    uint32_t pref, cur, intIP, low, high;
    if (NULL == p) {
        return NULL;
    }
    intIP = ip_ip2long(p, ip, &pref);;
    low = p->prefStart[pref];
    high = p->prefEnd[pref];
    cur = (low == high) ? low : ip_binary_search(p, low, high, intIP);
    if (cur == 100000000) {
        char *nil = (char *) malloc(2 * sizeof(char));
        nil[0] = '|';
        nil[1] = '\0';
        return nil;
    }
    return get_addr(p, cur);
}

long ip_binary_search(geo_ip *p, long low, long high, long k) {
    long M = 0;
    while (low <= high) {
        long mid = (low + high) / 2;
        int j = 2052 + (mid * 9);
        uint32_t endipNum = ip_read_int32(p,  j);
        if (endipNum >= k) {
            M = mid;
            if (mid == 0) {
                break;
            }
            high = mid - 1;
        } else
            low = mid + 1;
    }
    return M;
}

char *get_addr(geo_ip *p, uint32_t j) {
    uint32_t index = 2052 + j * 9;
    uint32_t offset = ip_read_int32(p,  index + 4);
    uint32_t length = p->buffer[index + 8];
    try {
        char *result = (char *) malloc((length + 1) * sizeof(char));
        memcpy(result, p->buffer + offset, length);
        result[length] = '\0';
        return result;
    }catch (...) {
        char* result = (char *) malloc((18 + 1) * sizeof(char));
        strcpy(result, "||||||||||||||||||");
        return result;
    }

}

uint32_t ip_ip2long(geo_ip *p, const char *addr, uint32_t *prefix) {
    uint32_t c, octet, t;
    uint32_t ipnum;
    int i = 3;

    octet = ipnum = 0;
    while ((c = *addr++)) {
        if (c == '.') {
            ipnum <<= 8;
            ipnum += octet;
            i--;
            octet = 0;
        } else {
            t = octet;
            octet <<= 3;
            octet += t;
            octet += t;
            c -= '0';

            octet += c;
            if (i == 3) {
                *prefix = octet;
            }
        }
    }

    ipnum <<= 8;

    return ipnum + octet;
}

uint32_t ip_read_int32(geo_ip *p, int pos) {
    uint32_t result;
    result = (uint32_t) ((p->buffer[pos]) | (p->buffer[pos + 1] << 8) | (p->buffer[pos + 2] << 16) | (p->buffer[pos + 3] << 24));
    return result;
}

